# Standard Library
import signal
import os
import time
import urllib
import sys, ConfigParser
import logging
import urlparse
from StringIO import StringIO
from Queue import Queue
import gtk

class VolumeNotLibRayError(Exception):
    """
    Trying to open a volume which appears not to be a Lib-Ray compliant video volume.
    """
    pass

# For python 2.5 and earlier, would need to install the separate "simplejson" module
#from simplejson import dumps as to_json
#from simplejson import loads as from_json
#
# For David Baird's GTK/WebKit code
from json import dumps as to_json
from json import loads as from_json
from webgui import start_gtk_thread, launch_browser, kill_gtk_thread
from webgui import synchronous_gtk_message, asynchronous_gtk_message


# Lib-Ray internals
from utils import lazytype
import server
import video, menu
from menu import SA, RC

PLAYER_VERSION = (0, 1)
LIBRAY_VERSION = (0, 5)

default_configuration = StringIO("""\
[LibRayMenu]                                                                                                                                                                           
languages: 0
locales: 0
access:
region: TE

[LibRayVideo]                                                                                                                                                                          
audio: 0
video: 0
subtitle: 0                                                                                                                                                                               
show_subtitles: no
chapter: 0                                                                                                                                                                            
bookmark: 0.0                                                                                                                                                                         
pixel_aspect: 1.0
display_width: 1920
display_height: 1080
display_aspect: 1.78
""")

ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.addHandler(ch)

server.logger.addHandler(ch)
server.logger.setLevel(logging.DEBUG)

class LibRayVolume(object):
    """
    @ivar version: Version of Lib-Ray this volume conforms to in X.Y format.
    
    @ivar producer: Unique name of producer (apply to Lib-Ray.com to get a free one and avoid collisions).
    
    @ivar release: Unique name of this release, assigned by the producer (Together, the producer+release is a unique global identifier for the release).
    
    @ivar title: Display title for the volume (as shown to the viewer).
    
    @ivar cover: Filename of cover image -- should be "cover.jpg" or "cover.png". Image spec is 600x900 (potrait-style).
    """
    def __init__(self, lib_ray_path):
        """
        Represents a Lib-Ray volume (disk or card) and associated metadata.
        """
        _meta = ConfigParser.ConfigParser()
        try:
          _meta.read(os.path.join(lib_ray_path, 'meta.cnf'))
        except:
          raise VolumeNotLibRayError, "No 'meta.cnf' file found."
        try:
          # Get basic, mandatory metadata
          # (If this isn't here, then it's not a Lib-Ray volume)
          try:
            version_strings = _meta.get('Lib-Ray', 'Version').split('.')
          except:
            version_strings = _meta.get('Lib-Ray', 'LibRayVersion').split('.')
          self.version    = (int(version_strings[0]), int(version_strings[1]))
          if self.version <= (0, 3):
              self.producer = _meta.get('Lib-Ray', 'LibRayID')
              self.release  = _meta.get('Lib-Ray', 'DiskID')
          else:
              self.producer   = _meta.get('Lib-Ray', 'Producer')
              self.release    = _meta.get('Lib-Ray', 'Release')
          self.title      = _meta.get('Lib-Ray', 'Title')
          self.cover      = _meta.get('Lib-Ray', 'Cover')
        except Exception as e:
          print e
          raise VolumeNotLibRayError, "Missing required metadata (Version, Producer, Release, Title, Cover)"
        
        # TODO: Cover image should be validated and made available to an internal database of
        #       volumes that the player knows about. Derivative versions might implement an
        #       internal catalog and/or library of videos.
        
        if self.version[0] > LIBRAY_VERSION:
            # The volume advertises a later version than the player knows about
            logger.warning("Lib-Ray volume is a later version than the player. Trying to play anyway.")
               
        # TODO: Most of the time, playing a later version probably won't be a serious problem, but
        #       there might be incompatibilities, so we need to warn the user that an upgrade might
        #       be needed. We should do this in a more graphical way.
        
        # Register the media files in the /Media directory
        # TODO: requires deep fixes in the video module -- come back to this
        #self.media = {}
        #for fname in os.listdir(os.path.join(lib_ray_path, 'Media')):
        #    self.media[fname] = video.register_video(os.path.join(lib_ray_path, 'Media', fname))
       

class LibRayPlayer(object):
    """
    Top-level object representing the running Lib-Ray software player.
    """
    #server_location = 'http://localhost:8765/'
    
    def __init__(self, config='/etc/lrplayer/player.cnf', port=8765):
        """
        Initialize the player with startup settings.
        """
        _cfg = ConfigParser.ConfigParser()
        _cfg.readfp(default_configuration)
        #_cfg.read(config)
        
        _cfg.write(sys.stdout)
        self.server_location = 'http://localhost:%4.4d' % port
        self.menu  = menu.Menu(_cfg)
        self.video = video.VideoPlayer(_cfg)
        
    def get_uri(self):
        return urlparse.urljoin(self.server_location, self.menu.relative_path())
        
    def load(self, lib_ray_path):
        """
        Given a path to a mounted Lib-Ray partition, load and queue the volume.
        """
        self.volume = LibRayVolume(lib_ray_path)
        self.uri = urlparse.urljoin(self.server_location, 'index.html')
        
        
class Global(object):
    quit = False
    @classmethod
    def set_quit(cls, *args, **kwargs):
        logger.debug("SETTING QUIT FLAG")
        cls.quit = True

def main(path):
    # TODO: This is an ugly kludge to work around the default
    # behavior of SimpleHTTPServer (which is to serve the current
    # directory). I'd rather modify the server to accept the
    # WWW home path as an argument.
    os.chdir(path)
    port = server.start_server_thread(Global.set_quit)
    lrplayer = LibRayPlayer(config='lr.cnf', port=port)
        
    # Set up the control vocabulary for dispatching from the web server
    # (Interfaces advertised by the video and menu objects)
    for name in lrplayer.menu.names:
        server.dispatcher.register(name, getattr(lrplayer.menu, 'set_'+name))

    for name in lrplayer.video.names:
        server.dispatcher.register(name, getattr(lrplayer.video,'set_'+name))
        
    server.dispatcher.register('quit', Global.set_quit)
    lrplayer.load(path)
    
    start_gtk_thread()
    #server.start_server_thread(Global.set_quit)

    # Point to the Lib-Ray server just launched
    uri = lrplayer.get_uri()
    
    keyqueue = Queue()
    
    browser = synchronous_gtk_message(launch_browser)(uri, quit_function=Global.set_quit, keyqueue=keyqueue)
    
    # System interrupt maps to normal quit function
    signal.signal(signal.SIGINT, Global.set_quit)
    
    # Main loop for the program
    # Note that the GTK main loop is running in a separate thread.

    while not Global.quit:
        # Catch 'Q' to quit the program (hotkey)
        if not keyqueue.empty():
            event = keyqueue.get()
            try:
               key = gtk.gdk.keyval_name(event.keyval)
               logger.debug("KEY PRESSED: %s" % key)
               if key in ('Q', 'q'):
                  Global.set_quit()
            except:
               logger.debug("KEY PRESSED EVENT: %s" % repr(event))
        
        # Process the server code (not threaded)
        # BUG: this call blocks until a request is made
        # which interferes with other processing (e.g. cannot quit until
        # a link is clicked)
        # I think this means I need to put the server in a separate thread?
        #server.httpd.handle_request()
        server.dispatcher.update()
        
    # Shutdown code
    logger.debug("EXITING...")
    Global.set_quit()
    kill_gtk_thread()
    server.kill_server_thread()
    logger.debug("Shutdown complete")
    

# I'm not certain, but I don't think there's really any reason
# to have this wrapper -- it's simpler to put the shutdown sequence
# in the "main()" function.

def my_quit_wrapper(fun):
    signal.signal(signal.SIGINT, Global.set_quit)
    def fun2(*args, **kwargs):
        try:
            x = fun(*args, **kwargs) # equivalent to "apply"
        finally:
            kill_gtk_thread()
            Global.set_quit()
        return x
        logger.debug("EXITING... in 'my_quit_wrapper'")
    return fun2


if __name__=='__main__':
    if len(sys.argv) < 2:
        print "USAGE: lr_play /path/to/libray/release"
    else:
        lib_ray_path = sys.argv[1]
        print "Opening volume at '%s'" % lib_ray_path
        main(lib_ray_path)
